package manager

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/go-resty/resty/v2"
	"io"
	"net/http"
	"net/url"
	"sieve_engine/execute"
	"sieve_engine/lib/config"
	"sieve_engine/task"
	"sieve_engine/utils"
	"strconv"
	"strings"
	"time"
)

type linkedinExecutor struct {
	*execute.DefaultExecutor
	execute.DefaultNetWorkConfig
}

var (
	linkExecutor execute.TaskExecutorInterface = &linkedinExecutor{
		DefaultExecutor: &execute.DefaultExecutor{
			Debug:                *config.Bool("sieve.linkedin.debug", true, "debug"),
			PrintMore:            *config.Bool("sieve.linkedin.print_more", true, "print more"),
			ProxyEnable:          *config.Bool("sieve.linkedin.proxy.enable", true, "proxy enable"),
			ProxyDebug:           *config.Bool("sieve.linkedin.proxy.debug", true, "proxy debug"),
			NetworkErrRetryCount: *config.Int("sieve.linkedin.proxy.retry_count_if_network_error", -1, "retry count if network error"),
			DefaultResultCache:   execute.NewDefaultResultCache(),
		},
	}
)

const (
	linkedinCheckpointUrl       = "https://www.linkedin.com/checkpoint/lg/login"
	linkedinLoginSubmitUrl      = "https://www.linkedin.com/checkpoint/lg/login-submit"
	linkedinPasswordResetSubmit = "https://www.linkedin.com/checkpoint/rp/request-password-reset-submit?sessionRedirect=https%3A%2F%2Fwww%2Elinkedin%2Ecom%2Fnhome%3FLOGIN_RESULT%3DPASS"
	exitAccountNoPassword       = "Your phone is not set up for password recovery"
	couldNotFindAnAccount       = "We couldn’t find an account"
)

func init() {
	execute.Register("linkedin", linkExecutor)
}

func (s *linkedinExecutor) Execute(params execute.TaskExecutorParams, taskProgress *task.Result) error {

	return s.DefaultExecutor.Execute(params, taskProgress, s.getApi2)
}

func (s *linkedinExecutor) getNetWorkConfig() *execute.NetWorkConfig {
	return &execute.NetWorkConfig{
		TransPortConfig: execute.TransPortConfig{
			DialTimeOut:           time.Second * 3,
			ReadTimeOut:           time.Second * 60,
			WriteTimeOut:          time.Second * 60,
			ConnTimeOut:           time.Second * 60,
			IdleConnTimeout:       time.Second * 90,
			ExpectContinueTimeout: time.Second * 2,
			TLSHandshakeTimeout:   time.Second * 20,
			DisableKeepAlives:     true,
			TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
		},
		HttpConfig: execute.HttpConfig{
			Timeout: time.Second * 5,
		},
	}
}

func (s *linkedinExecutor) getApi2(phone string, params execute.TaskExecutorParams, roundTripper http.RoundTripper, reuseProxy bool) (http.RoundTripper, bool, *url.URL, error) {

	if s.Debug {
		return nil, true, nil, nil
	}

	var (
		proxyUrl  *url.URL
		client    = resty.New()
		urlParams = url.Values{
			"errorKey": []string{"unexpected_error"},
		}
		bodyData  = strings.NewReader(urlParams.Encode())
		userAgent = s.getRandVersionUserAgent()
	)

	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyUrl = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	s.buildHeaderStep1(client.Header, len(urlParams), userAgent)
	client.SetCloseConnection(true)
	client.SetTransport(roundTripper)
	resp, err := client.SetDebug(s.ProxyDebug).R().SetBody(bodyData).Get(linkedinCheckpointUrl)
	if err != nil {
		return roundTripper, false, proxyUrl, err
	}

	if resp.StatusCode() != http.StatusOK {
		return roundTripper, false, proxyUrl, errors.New(fmt.Sprintf("http failed,code:%d", resp.StatusCode()))
	}

	pageBean := NewLoginPage()
	pageBean.UserAgent = userAgent
	pageBean.LiUuid = resp.Header().Get("X-Li-Uuid")
	pageBean.FsUuid = resp.Header().Get("X-Fs-Uuid")
	pageBean.Cookies = resp.Header().Values("Set-Cookie")

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body()))
	if err != nil {
		return roundTripper, false, proxyUrl, execute.RequestErrorRefusedError
	}

	inputNodes := doc.Find("input").Nodes
	if inputNodes != nil {
		for _, inputNode := range inputNodes {
			inputName := ""
			inputValue := ""
			for _, a := range inputNode.Attr {
				if a.Key == "name" {
					inputName = a.Val
				} else if a.Key == "value" {
					inputValue = a.Val
				}
			}
			if inputName == "csrfToken" {
				pageBean.CsrfToken = inputValue
			} else if inputName == "pageInstance" {
				pageBean.PageInstance = inputValue
			} else if inputName == "loginCsrfParam" {
				pageBean.LoginCsrfParam = inputValue
			} else if inputName == "fp_data" {
				pageBean.FpData = inputValue
			} else if inputName == "apfc" {
				pageBean.Apfc = inputValue
			} else if inputName == "sIdString" {
				pageBean.SIdString = inputValue
			}
			pageBean.InputItems[inputName] = inputValue
		}
	}

	apfc, ok := s.requestApfc(pageBean.PageInstance, pageBean.SIdString, userAgent)
	if ok && apfc != "" && apfc != "{}" {
		pageBean.Apfc = apfc
	}

	var (
		dataRecoverBody = s.buildData(phone, pageBean)
	)
	client.Header = http.Header{}
	s.buildDataStep2(client.Header, pageBean, len(dataRecoverBody))
	resp, err = client.SetDebug(s.ProxyDebug).R().SetBody(dataRecoverBody).Post(linkedinLoginSubmitUrl)
	if err != nil {
		return roundTripper, false, proxyUrl, err
	}

	/*
		checkpoint/lg/login
		checkpoint/lg/login-submit
	*/
	urlPath := resp.RawResponse.Request.URL.Path
	if !strings.Contains(urlPath, "login") { // 验证码，去恢复密码方式
		if resp != nil {
			resp = nil
		}

		client.Header = http.Header{}
		bodyStep3 := s.buildDataStep3(phone, pageBean)
		s.buildHeadersStep3(client.Header, pageBean, len(dataRecoverBody))
		resp, err = client.SetDebug(s.ProxyDebug).R().SetBody(bodyStep3).Post(linkedinPasswordResetSubmit)
		if err != nil {
			return roundTripper, false, proxyUrl, err
		}

		if resp.StatusCode() != http.StatusOK {
			return roundTripper, false, proxyUrl, errors.New(fmt.Sprintf("http failed,code:%d", resp.StatusCode()))
		}

		if b, err := s.parseResponseStep3(resp.Body()); err != nil {
			return roundTripper, false, proxyUrl, err
		} else {
			return roundTripper, b, proxyUrl, nil
		}
	}

	b, err := s.parseResponseStep2(resp.Body())
	if err != nil {
		return roundTripper, false, proxyUrl, err
	}

	return roundTripper, b, proxyUrl, nil
}

func (s *linkedinExecutor) buildDataStep3(number string, pageBean *LoginPage) string {
	values := url.Values{
		"csrfToken":      []string{pageBean.CsrfToken},
		"pageInstance":   []string{pageBean.PageInstance},
		"userName":       []string{number},
		"encryptedEmail": []string{pageBean.EncryptedEmail}, // "false"
		"fp_data":        []string{pageBean.FpData},         // "default"
		"trk":            []string{"hb_signin"},
		"apfc":           []string{pageBean.Apfc},
	}
	return values.Encode()
}

func (s *linkedinExecutor) buildHeadersStep3(header http.Header, pageBean *LoginPage, dataLen int) {
	header.Add("Host", "www.linkedin.com")
	header.Add("Cache-Control", "max-age=0")
	header.Add("Upgrade-Insecure-Requests", "1")
	header.Add("Origin", "https://www.linkedin.com")
	header.Add("content-type", "application/x-www-form-urlencoded")
	// header.Add("User-Agent", "LIAuthLibrary:0.0.3 com.linkedin.android:4.1.823 Google_Pixel 3:android_10")
	// header.Add("X-Requested-With", "com.linkedin.android")
	header.Add("User-Agent", pageBean.UserAgent)
	header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
	header.Add("Sec-Fetch-Site", "same-origin")
	header.Add("Sec-Fetch-Mode", "navigate")
	header.Add("Sec-Fetch-User", "?1")
	header.Add("Sec-Fetch-Dest", "document")
	// header.Add("Referer", "https://www.linkedin.com/checkpoint/rp/request-password-reset?session_redirect=https%3A%2F%2Fwww.linkedin.com%2Fnhome%3FLOGIN_RESULT%3DPASS")
	header.Add("Referer", "https://www.linkedin.com/checkpoint/lg/login")
	header.Add("Accept-Language", "en-US,en;q=0.9") // "zh-CN,zh;q=0.9,en-AI;q=0.8,en-XA;q=0.7,en-US;q=0.6,en;q=0.5"
	header.Add("Accept-Encoding", "gzip, deflate")
	// header.Add("Cookie", "JSESSIONID=\"ajax:5022186284632989593\"; bcookie=\"v=2&12e9da0e-801e-49ff-8624-ed90a99998a1\"; bscookie=\"v=1&20230527095129351be621-ea8e-4b11-8e8e-28521dd13db0AQEBekfrlQbOlJ3-eSNZE74MIy-Sshh8\"; lang=\"v=2&lang=zh_CN\"; lidc=\"b=OGST08:s=O:r=O:a=O:p=O:g=2573:u=1:x=1:i=1685187587:t=1685273987:v=2:sig=AQFFp97FB677sgLaUeJ9Ju4hhHq0EJJ5\"; lang=v=2&lang=zh-cn")

	for _, cookie := range pageBean.Cookies {
		header.Add("Cookie", cookie)
	}

	header.Add("Connection", "close")
	header.Add("Content-Length", strconv.Itoa(dataLen))
}

func (s *linkedinExecutor) parseResponseStep2(body []byte) (bool, error) {

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
	if err != nil {
		return false, execute.RequestErrorNetError
	}

	errorPassword := doc.Find("div[id=error-for-password]").First()
	if errorPassword != nil {
		txt := errorPassword.Text()
		if txt != "" {
			/*
				That’s not the right password. Forgot password?
			*/
			return true, nil
		}
	}

	errorUsername := doc.Find("div[id=error-for-username]").First()
	if errorUsername != nil {
		txt := errorUsername.Text()
		if txt != "" {
			/*
				Couldn't find a LinkedIn account associated with this phone number. Try again or\n   create an account
			*/
			return false, nil
		}
	}

	return false, execute.RequestErrorNetError
}

func (s *linkedinExecutor) parseResponseStep3(body []byte) (bool, error) {

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
	if err != nil {
		return false, execute.RequestErrorNetError
	}

	/*
		1. 号码不存在 <meta name="pageKey" content="p_checkpoint_rp_requestPasswordReset">
		2. 号码存在发送验证码 <meta name="pageKey" content="p_checkpoint_ch_registerPhoneChallenge">
		3. 滑块验证 <meta name="pageKey" content="p_checkpoint_ch_captchaV2Challenge">
		4. 号码存在验证名字 <meta name="pageKey" content="p_checkpoint_rp_nameVerification">

	*/

	pageKey, exist := doc.Find("meta[name=pageKey]").First().Attr("content")
	if exist {
		if strings.Contains(pageKey, "checkpoint_rp_nameVerification") {
			// 号码存在，继续验证，最后一步，验证您的身份（认证名字）
			return true, nil
		} else if strings.Contains(pageKey, "checkpoint_ch_captchaV2Challenge") {
			// 请进行快速的安全验证, 人机验证设为无效
			return false, nil
		} else if strings.Contains(pageKey, "checkpoint_rp_requestPasswordReset") {
			bannerAlertSpan := doc.Find(".body__banner--error span[role=alert]").First()
			alertTxt := bannerAlertSpan.Text()
			if strings.Contains(alertTxt, couldNotFindAnAccount) {
				return false, nil
			}
			if strings.Contains(alertTxt, exitAccountNoPassword) {
				return true, nil
			}
			// Something unexpected happened. Please try again
			if strings.Contains(alertTxt, "Please try again") {
				return false, execute.RequestErrorIpLimitedError
			}
		} else if strings.Contains(pageKey, "checkpoint_ch_registerPhoneChallenge") {
			// 号码存在，验证码已发送到您的手机
			return true, nil
		} else if strings.Contains(pageKey, "checkpoint_rp_resetProhibited") {
			return true, nil
		}
	}
	_, exit := doc.Find("body[id=error404]").First().Attr("dir")
	if exit {
		return false, execute.RequestErrorIpLimitedError
	}

	titleNode := doc.Find("title").First()
	if titleNode != nil && strings.Contains(titleNode.Text(), "Unauthorized") {
		return false, execute.RequestErrorUnauthorized
	}
	if titleNode != nil && strings.Contains(titleNode.Text(), "Error") {
		return false, execute.RequestErrorIpLimitedError
	}
	if titleNode != nil && strings.Contains(titleNode.Text(), "404 Not Found") {
		return false, execute.RequestErrorIpLimitedError
	}
	if titleNode != nil && strings.Contains(titleNode.Text(), "999: request failed") {
		return false, execute.RequestErrorIpLimitedError
	}
	if titleNode != nil && titleNode.Text() == "Доступ ограничен" { // 访问受限
		return false, execute.RequestErrorIpLimitedError
	}
	if titleNode != nil && titleNode.Text() == "502: Server Hangup" { // 访问受限
		return false, execute.RequestErrorIpLimitedError
	}

	return false, execute.RequestErrorIpLimitedError
}

func (s *linkedinExecutor) getApi(phone string, params execute.TaskExecutorParams, roundTripper http.RoundTripper, reuseProxy bool) (http.RoundTripper, bool, *url.URL, error) {

	valid, u, err := s.getReady(phone, params, roundTripper, reuseProxy)
	return nil, valid, u, err
}

func (s *linkedinExecutor) getRandVersionUserAgent() string {
	appleWebKitVersion := strconv.Itoa(utils.RandInt(20, 35))
	chromeVersion := strconv.Itoa(utils.RandInt(100, 114))
	// req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36")
	serAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/5" + appleWebKitVersion + ".36 (KHTML, like Gecko) Chrome/" + chromeVersion + ".0.0.0 Safari/537.36"
	return serAgent
}

func (s *linkedinExecutor) buildHeaderStep1(header http.Header, dataLen int, ua string) {
	header.Add("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
	header.Add("Accept-Encoding", "gzip, deflate, br")
	header.Add("accept-language", "en-US,en;q=0.9")
	header.Add("Cache-Control", "max-age=0")
	header.Add("referer", "https://www.linkedin.com/")
	header.Add("Sec-Ch-Ua", "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Microsoft Edge\";v=\"114\"")
	header.Add("Sec-Ch-Ua-Mobile", "?0")
	header.Add("Sec-Ch-Ua-Platform", "\"Windows\"")
	header.Add("Sec-Fetch-Dest", "document")
	header.Add("Sec-Fetch-Mode", "navigate")
	header.Add("Sec-Fetch-Site", "same-origin")
	header.Add("Sec-Fetch-User", "?1")
	header.Add("Upgrade-Insecure-Requests", "1")
	if ua == "" {
		ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
	}
	header.Add("User-Agent", ua)

	header.Add("Connection", "close")
	header.Add("Content-Length", strconv.Itoa(dataLen))
}

func (s *linkedinExecutor) requestApfc(pageInstance, sIdString, ua string) (string, bool) {
	apfc := "{\"df\":{\"a\":\"s24xT6M9kXt6wAgfcPRI7A==\",\"b\":\"kWvp0JQrKGwhVTIJZlZxRkMlar3n0XCeUt6AI26MAYmytnCfjG8RLASDvemAEoSowZcS1vNwwyWrGy2TVIy6p5W6/j2H9FR2PvMvov6lwUOxm7wCEdgzR0Ieq9+il8klln6CqBdMtJ5pnPjeiDeUVIjIrmLXhpm016PUZmlVjv91m1h8C2in+YPvvVQVLrAmIyJZYzI0cWbGGBazN1caAdsN95LIGavcvFDOlDXGjjed+0j4N1ZFBW0hQvx53QdimULXfta23dIhz8VQecF17m43UCXHzu2xlAJ1EqwCsEfH12++OyTV7eiheTvP8bwvBIeQFnL1Ddig1TSGa2NllQ==\",\"c\":\"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\",\"d\":9,\"e\":2}}"
	return apfc, true
}

func (s *linkedinExecutor) buildData(number string, pageBean *LoginPage) string {
	pageInstance := pageBean.InputItems["pageInstance"]
	sIdString := pageBean.InputItems["sIdString"]
	values := url.Values{
		"csrfToken":             []string{pageBean.InputItems["csrfToken"]},
		"session_key":           []string{number},
		"ac":                    []string{"0"},
		"sIdString":             []string{sIdString},
		"parentPageKey":         []string{pageBean.InputItems["parentPageKey"]},
		"pageInstance":          []string{pageInstance},
		"trk":                   []string{""},
		"authUUID":              []string{""},
		"session_redirect":      []string{""},
		"loginCsrfParam":        []string{pageBean.InputItems["loginCsrfParam"]},
		"fp_data":               []string{pageBean.InputItems["fp_data"]},
		"apfc":                  []string{pageBean.Apfc},
		"_d":                    []string{pageBean.InputItems["_d"]},
		"showGoogleOneTapLogin": []string{"true"},
		"controlId":             []string{pageBean.InputItems["controlId"]},
		"session_password":      []string{utils.RandomString(10)},
	}
	return values.Encode()
}

func (s *linkedinExecutor) buildDataStep2(header http.Header, pageBean *LoginPage, dataLen int) {
	header.Add("authority", "www.linkedin.com")
	header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
	header.Add("accept-language", "en-US,en;q=0.9")
	header.Add("Cache-Control", "max-age=0")
	header.Add("content-type", "application/x-www-form-urlencoded")
	header.Add("Origin", "https://www.linkedin.com")
	header.Add("Referer", "https://www.linkedin.com/checkpoint/lg/login?errorKey=unexpected_error")
	header.Add("Sec-Ch-Ua", "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Microsoft Edge\";v=\"114\"")
	header.Add("sec-ch-ua-mobile", "?0")
	header.Add("sec-ch-ua-platform", "\"Windows\"")
	header.Add("Sec-Fetch-Dest", "document")
	header.Add("Sec-Fetch-Mode", "navigate")
	header.Add("Sec-Fetch-Site", "same-origin")
	header.Add("Sec-Fetch-User", "?1")
	header.Add("upgrade-insecure-requests", "1")
	header.Add("User-Agent", pageBean.UserAgent)
	header.Add("Accept-Encoding", "gzip, deflate")
	// header.Add("Cookie", "JSESSIONID=\"ajax:5022186284632989593\"; bcookie=\"v=2&12e9da0e-801e-49ff-8624-ed90a99998a1\"; bscookie=\"v=1&20230527095129351be621-ea8e-4b11-8e8e-28521dd13db0AQEBekfrlQbOlJ3-eSNZE74MIy-Sshh8\"; lang=\"v=2&lang=zh_CN\"; lidc=\"b=OGST08:s=O:r=O:a=O:p=O:g=2573:u=1:x=1:i=1685187587:t=1685273987:v=2:sig=AQFFp97FB677sgLaUeJ9Ju4hhHq0EJJ5\"; lang=v=2&lang=zh-cn")

	for _, cookie := range pageBean.Cookies {
		header.Add("Cookie", cookie)
	}

	header.Add("Connection", "close")
	header.Add("Content-Length", strconv.Itoa(dataLen))
}

func (s *linkedinExecutor) recover(phone string, page *LoginPage, roundTripper http.RoundTripper, reuseProxy bool) (bool, *url.URL, error) {

	data := s.buildData(phone, page)
	bodyReader := strings.NewReader(data)

	req, err := http.NewRequest("POST", linkedinLoginSubmitUrl, bodyReader)
	if err != nil {
		return false, nil, err
	}

	s.buildDataStep2(req.Header, page, len(data))

	req.Close = true
	var (
		httpClient = &http.Client{Timeout: time.Second * 8}
		proxyURL   *url.URL
	)

	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	httpClient.Transport = roundTripper
	response, err := httpClient.Do(req)
	if err != nil {
		return false, nil, err
	}

	if response.StatusCode != http.StatusOK {
		return false, nil, errors.New(fmt.Sprintf("http failed,%v", response.StatusCode))
	}

	contentBytes, err := io.ReadAll(response.Body)
	if err != nil {
		return false, nil, execute.RequestErrorNetError
	}
	isGzip := response.Header.Get("Content-Encoding") == "gzip"
	if isGzip {
		contentBytes, err = utils.GzipDecode(contentBytes)
		if err != nil {
			return false, nil, execute.RequestErrorNetError
		}
	}

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(contentBytes))
	if err != nil {
		return false, nil, execute.RequestErrorNetError
	}

	errorPassword := doc.Find("div[id=error-for-password]").First()
	if errorPassword != nil {
		txt := errorPassword.Text()
		if txt != "" {
			return true, nil, nil
		}
	}

	errorUsername := doc.Find("div[id=error-for-username]").First()
	if errorUsername != nil {
		txt := errorUsername.Text()
		if txt != "" {
			return false, nil, nil
		}
	}

	return false, proxyURL, execute.RequestErrorIpLimitedError
}

func (s *linkedinExecutor) getReady(phone string, taskParams execute.TaskExecutorParams, roundTripper http.RoundTripper, reuseProxy bool) (bool, *url.URL, error) {

	var (
		params = url.Values{
			"errorKey": []string{"unexpected_error"},
		}
		bodyData = strings.NewReader(params.Encode())
	)

	req, err := http.NewRequest("GET", linkedinCheckpointUrl, bodyData)
	if err != nil {
		return false, nil, err
	}
	agent := s.getRandVersionUserAgent()
	s.buildHeaderStep1(req.Header, len(params), agent)

	req.Close = true
	httpClient := &http.Client{Timeout: time.Second * 5}
	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, _ = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	httpClient.Transport = roundTripper
	response, err := httpClient.Do(req)
	if err != nil {
		return false, nil, err
	}
	if response.StatusCode != http.StatusOK {
		return false, nil, errors.New(fmt.Sprintf("http response status code %d", response.StatusCode))
	}

	contentBytes, err := io.ReadAll(response.Body)
	if err != nil {
		return false, nil, execute.RequestErrorNetError
	}

	isGzip := response.Header.Get("Content-Encoding") == "gzip"
	if isGzip {
		contentBytes, err = utils.GzipDecode(contentBytes)
		if err != nil {
			return false, nil, execute.RequestErrorNetError
		}
	}

	pageBean := NewLoginPage()
	pageBean.UserAgent = agent
	pageBean.LiUuid = response.Header.Get("X-Li-Uuid")
	pageBean.FsUuid = response.Header.Get("X-Fs-Uuid")
	pageBean.Cookies = response.Header.Values("Set-Cookie")

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(contentBytes))
	if err != nil {
		return false, nil, execute.RequestErrorRefusedError
	}

	inputNodes := doc.Find("input").Nodes
	if inputNodes != nil {
		for _, inputNode := range inputNodes {
			inputName := ""
			inputValue := ""
			for _, a := range inputNode.Attr {
				if a.Key == "name" {
					inputName = a.Val
				} else if a.Key == "value" {
					inputValue = a.Val
				}
			}
			if inputName == "csrfToken" {
				pageBean.CsrfToken = inputValue
			} else if inputName == "pageInstance" {
				pageBean.PageInstance = inputValue
			} else if inputName == "loginCsrfParam" {
				pageBean.LoginCsrfParam = inputValue
			} else if inputName == "fp_data" {
				pageBean.FpData = inputValue
			} else if inputName == "apfc" {
				pageBean.Apfc = inputValue
			} else if inputName == "sIdString" {
				pageBean.SIdString = inputValue
			}
			pageBean.InputItems[inputName] = inputValue
		}
	}
	apfc, ok := s.requestApfc(pageBean.PageInstance, pageBean.SIdString, agent)
	if ok && apfc != "" && apfc != "{}" {
		pageBean.Apfc = apfc
	}
	// area code required

	return s.recover(phone, pageBean, roundTripper, reuseProxy)
}
